﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Collections.ObjectModel;
using System.Runtime.Serialization.Json;
using System.ComponentModel;
using WTwitter.Model.UserData;
using WTwitter.Model.Utility;

namespace WTwitter.Model.Framework {


	/// <summary>
	/// タイムラインを管理するクラス
	/// </summary>
	public class Timeline : ITimeline {
		#region private member
		private IItemsReader _itemsReader;
		private List<ITimelineItem> _allItems = new List<ITimelineItem>();
		private AuthInfo _authInfo;
		private bool _isAuthRequired;
		private BackgroundWorker _backgroundWorker;
		private TimelineSetting _setting;
		private bool _isUpdating = false;
		private object _stateUpdateLock = new object();
		#endregion

		#region Constructor
		/// <summary>
		/// コンストラクタ
		/// </summary>
		/// <param name="timelineURL">タイムラインを取得するためのURL</param>
		public Timeline(TimelineSetting setting, IItemsReader reader)
			: this(setting, reader, null) {
		}

		/// <summary>
		/// コンストラクタ
		/// </summary>
		/// <param name="timelineURL">タイムラインを取得するためのURL</param>
		/// <param name="reader">情報源からのstreamをタイムラインのアイテムに変換するreader</param>
		/// <param name="info">ユーザー情報</param>
		public Timeline(TimelineSetting setting, IItemsReader reader, AuthInfo info) {
			_setting = setting;
			_itemsReader = reader;
			_isAuthRequired = setting.IsAuthRequired;
			_authInfo = info;
			TimelineRead += (sender, e) => { };
		}
		#endregion

		#region Property
		/// <summary>
		/// タイムライン名
		/// </summary>
		protected string Name {
			get { return _setting.Name; }
		}

		/// <summary>
		/// タイムラインのURL
		/// </summary>
		protected string Url {
			get { return _setting.Url; }
		}

		/// <summary>
		/// タイムラインに含まれるすべてのアイテム
		/// </summary>
		public List<ITimelineItem> AllItems {
			get { return _allItems; }
		}

		/// <summary>
		/// タイムラインが取得できる状態にあるかどうか
		/// </summary>
		public bool IsUpdatable {
			get {
				if (!_isAuthRequired) return true;

				switch (_authInfo.AuthState) {
					case AuthState.Verified:
						return true;
					case AuthState.FailedToVerify:
						return false;
					case AuthState.NeverVerified:
						return _authInfo.VerifyCredential();
					default:
						throw new InvalidOperationException();
				}
			}
		}

		public bool IsUpdating {
			get {
				return _isUpdating;
			}
		}

		/// <summary>
		/// 認証のためのユーザー情報
		/// </summary>
		public AuthInfo AuthInfo {
			get { return _authInfo; }
		}

		#endregion

		#region Public Method
		/// <summary>
		/// タイムラインを更新（非同期動作なので結果を待たず戻る）
		/// </summary>
		public virtual void UpdateAsync() {
			lock (_stateUpdateLock) {
				if (_isUpdating) {
					return;
				}
				_isUpdating = true;
			}

			_backgroundWorker = new BackgroundWorker();
			_backgroundWorker.DoWork += this.getItemsAsync;
			_backgroundWorker.RunWorkerCompleted += this.getItemsCompleted;
			_backgroundWorker.RunWorkerCompleted += (sender, e) => {
				lock (_stateUpdateLock) {
					//T.B.D.他のdelegateなどで呼ばれなかったときにfalseに戻らなくなるので対策が必要
					_isUpdating = false;
				}
			};
			_backgroundWorker.RunWorkerAsync();
		}

		public virtual void ForceUpdate() {
			UpdateAsync();
		}

		/// <summary>
		/// BackgroundWorkerのDoWork
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		protected virtual void getItemsAsync(object sender, DoWorkEventArgs e) {
			//タイムラインからアイテムを取得する
			//※別スレッドで実行される
			var info = _isAuthRequired ? _authInfo : null;

			try {
				using (var response = WebUtility.GetResponse(Url, "GET", new RequestParameter[] { }, info))
				using (var stream = response.GetResponseStream()) {
					e.Result = _itemsReader.Read(stream).ToList<ITimelineItem>();
				}
			} catch (Exception exception) {
				e.Result = exception;
			}
		}

		/// <summary>
		/// BackgroundWorkerのCompleted
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		protected virtual void getItemsCompleted(object sender, RunWorkerCompletedEventArgs e) {
			//取得したアイテムを通知する
			//※UIスレッドで実行される
			var exception = e.Result as Exception;
			if (exception != null) {
				OnReadItems(new ReadTimelineEventArgs(exception));
				return;
			}

			var result = e.Result as List<ITimelineItem>;
			var newItems = new List<ITimelineItem>();
			foreach (var item in result) {
				if (!AllItems.Contains(item)) {
					newItems.Add(item);
				}
			}
			OnReadItems(new ReadTimelineEventArgs(newItems));
		}
		#endregion

		#region protected method
		protected void OnReadItems(ReadTimelineEventArgs e) {
			if (e.ResultType == TimelineReadResult.ReadNewItems) {
				AllItems.AddRange(e.NewItems);
			}
			TimelineRead(this, e);
		}
		#endregion

		#region Event
		/// <summary>
		/// タイムラインの読込が完了したことを通知するイベント（取得失敗時もイベント発生）
		/// </summary>
		public event EventHandler<ReadTimelineEventArgs> TimelineRead;
		#endregion
	}
}
